# -*- coding: utf-8 -*-

#    OriginDB
#    Copyright (C) 2009  Jason Power <power.jg@gmail.com>
#
#    This program is free software; you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation; either version 2 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License along
#    with this program; if not, write to the Free Software Foundation, Inc.,
#    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

from PyQt4 import QtCore, QtGui
from ui_reactionEdit import Ui_ReactionEditFrame

pressureConversions = {"atm":1, "Torr":760, "kPa":101.325}

class ReactionEdit(QtGui.QFrame):

	maxCompoundLength = 20
	
	def __init__(self, controller, reactionClass, catalystClass, equCompoundClass, mode='New'):
		QtGui.QFrame.__init__(self)

		self.controller = controller

		self.reactionClass = reactionClass
		self.catalystClass = catalystClass
		self.equCompoundClass = equCompoundClass

		self.reaction = None

        # Set up the user interface from Designer.
		self.ui = Ui_ReactionEditFrame()
		self.ui.setupUi(self)

		self.setMode(mode)

		self.setReadonly(not self.controller.userLoggedIn())

		self.reactants = []

		self.products = []

		self.catalysts = []
		self.catalystNames = []

		self.reference = None

		self.ui.pressureUnitsComboBox.addItems(["atm", "Torr", "kPa"])
		self.units = "atm"

		#hacks so I don't have to extend QListWidgetView
		self.ui.reactantList.dropEvent = self.customReactantDropEvent
		self.ui.productList.dropEvent = self.customProductDropEvent
		self.ui.catalystList.dropEvent = self.customCatalystDropEvent

		self.connect(self.ui.addPushButton, QtCore.SIGNAL("clicked()"), self.insertReaction)
		self.connect(self.ui.updatePushButton, QtCore.SIGNAL("clicked()"), self.updateReactionAction)

		self.connect(self.ui.searchReactantPushButton, QtCore.SIGNAL("clicked()"), self.searchReactant)
		self.connect(self.ui.searchProductPushButton, QtCore.SIGNAL("clicked()"), self.searchProduct)
		self.connect(self.ui.searchCatalystPushButton, QtCore.SIGNAL("clicked()"), self.searchCatalyst)
		self.connect(self.ui.newCatalystPushButton, QtCore.SIGNAL("clicked()"), self.newCatalyst)
		self.connect(self.ui.searchReferencePushButton, QtCore.SIGNAL("clicked()"), self.searchReference)
		self.connect(self.ui.newReferencePushButton, QtCore.SIGNAL("clicked()"), self.newReference)
		self.connect(self.ui.showAllCatalystsPushButton, QtCore.SIGNAL("clicked()"), self.showAllCatalysts)

		self.connect(self.ui.removeReactantPushButton, QtCore.SIGNAL("clicked()"), self.removeReactant)
		self.connect(self.ui.removeProductPushButton, QtCore.SIGNAL("clicked()"), self.removeProduct)
		self.connect(self.ui.editProductPushButton, QtCore.SIGNAL("clicked()"), self.editProduct)
		self.connect(self.ui.editReactantPushButton, QtCore.SIGNAL("clicked()"), self.editReactant)
		self.connect(self.ui.removeCatalystPushButton, QtCore.SIGNAL("clicked()"), self.removeCatalyst)
		
		self.connect(self.ui.pressureUnitsComboBox, QtCore.SIGNAL("currentIndexChanged(const QString &)"), self.pressureUnitChanged)

		self.connect(self.ui.reactantList, QtCore.SIGNAL("itemDoubleClicked (QListWidgetItem *)"), self.reactantDoubleClick)
		self.connect(self.ui.productList, QtCore.SIGNAL("itemDoubleClicked (QListWidgetItem *)"), self.productDoubleClick)

	def clear(self):
		self.products = []

		self.reactants = []

		self.catalysts = []
		self.catalystNames = []

		self.ui.reactantList.clear()
		self.ui.productList.clear()
		self.ui.catalystList.clear()

		self.ui.referenceLineEdit.clear()
		self.ui.refAuthorsLineEdit.clear()
		self.ui.refYearSpinBox.setValue(1800)
		self.ui.temperatureSpinBox.setValue(22)
		self.ui.pressureSpinBox.setValue(1)
		self.ui.pressureUnitsComboBox.setCurrentIndex(0)
		self.units = "atm"
		self.ui.daysSpinBox.setValue(0)
		self.ui.hoursSpinBox.setValue(0)
		self.ui.minutesSpinBox.setValue(0)
		self.ui.secondsSpinBox.setValue(0)
		self.ui.pHSpinBox.setValue(7)
		self.ui.solventLineEdit.clear()
		self.ui.solidCheckBox.setChecked(False)
		self.ui.liquidCheckBox.setChecked(False)
		self.ui.gasCheckBox.setChecked(False)
		self.ui.otherLineEdit.clear()

		if not self.ui.referenceUnknownCheckBox.isChecked():
			self.ui.referenceUnknownCheckBox.toggle()
		if not self.ui.temperatureUnknownCheckBox.isChecked():
			self.ui.temperatureUnknownCheckBox.toggle()
		if not self.ui.pressureUnknownCheckBox.isChecked():
			self.ui.pressureUnknownCheckBox.toggle()
		if not self.ui.timeUnknownCheckBox.isChecked():
			self.ui.timeUnknownCheckBox.toggle()
		if not self.ui.pHUnknownCheckBox.isChecked():
			self.ui.pHUnknownCheckBox.toggle()
		if not self.ui.solventUnknownCheckBox.isChecked():
			self.ui.solventUnknownCheckBox.toggle()
		if not self.ui.otherUnknownCheckBox.isChecked():
			self.ui.otherUnknownCheckBox.toggle()

		self.reaction = None

	def insertReaction(self):

		mess = ''
		if not len(self.reactants):
			mess += "No reactants provided\n"
		if not len(self.products):
			mess += "No products provided\n"
		if not len(str(self.ui.referenceLineEdit.text())) or self.ui.referenceUnknownCheckBox.isChecked():
			mess += "No reference provided\n"

		if mess:
			self.msgDialog = QtGui.QMessageBox(QtGui.QMessageBox.Warning, "Format Error", mess, QtGui.QMessageBox.Ignore)
			self.msgDialog.addButton(QtGui.QMessageBox.Cancel)
			ret = self.msgDialog.exec_()
			if ret == 4194304: #seems to be a magic number... bug in pyqt?
				return

		self.reaction = self.reactionClass()

		self.updateReaction()
		self.reaction.insert()
		
		if not self.readonly:
			self.ui.updatePushButton.show()

		self.msgDialog = QtGui.QMessageBox(QtGui.QMessageBox.Information, "Reaction added", "Reaction added successfully", QtGui.QMessageBox.Ok)
		self.msgDialog.show()
		
	def updateReactionAction(self):
		
		self.updateReaction()
		self.reaction.forceUpdate()
		
		self.msgDialog = QtGui.QMessageBox(QtGui.QMessageBox.Information, "Reaction updated", "Reaction updated successfully", QtGui.QMessageBox.Ok)
		self.msgDialog.show()

	def updateReaction(self):

		if not self.ui.temperatureUnknownCheckBox.isChecked():
			self.reaction.temperature = self.ui.temperatureSpinBox.value()
		if not self.ui.pressureUnknownCheckBox.isChecked():
			self.reaction.pressure = self.ui.pressureSpinBox.value() / pressureConversions[self.units]
		if not self.ui.timeUnknownCheckBox.isChecked():
			self.reaction.time = self.ui.minutesSpinBox.value()
			self.reaction.time += self.ui.secondsSpinBox.value()/60.0
			self.reaction.time += self.ui.hoursSpinBox.value()*60
			self.reaction.time += self.ui.daysSpinBox.value()*24*60
		if not self.ui.pHUnknownCheckBox.isChecked():
			self.reaction.pH = self.ui.pHSpinBox.value()
		if not self.ui.solventUnknownCheckBox.isChecked():
			self.reaction.solvent = str(self.ui.solventLineEdit.text())
		if self.ui.solidCheckBox.isChecked():
			self.reaction.solid = True
		if self.ui.liquidCheckBox.isChecked():
			self.reaction.liquid = True
		if self.ui.gasCheckBox.isChecked():
			self.reaction.gas = True
		if not self.ui.otherUnknownCheckBox.isChecked():
			self.reaction.other = str(self.ui.otherLineEdit.text())

		self.reaction.reactants = self.reactants
		
		self.reaction.products = self.products

		self.reaction.catalysts = self.catalysts

		if not self.ui.referenceUnknownCheckBox.isChecked():
			if self.reference:
				self.reaction.reference = self.reference

	def search(self):
		self.reaction = self.reactionClass()
		self.reaction.reactants = self.reactants

	def setCurrentReaction(self, reaction):
		if self.reaction:
			del self.reaction
		self.reaction = reaction
		
		self.products = reaction.products
		self.reactants = reaction.reactants
		
		print self.reactants
		print self.products
		
		self.catalysts = reaction.catalysts

		self.updateFields()
		
		if not self.readonly:
			self.ui.updatePushButton.show()


	def updateFields(self):
		for react in self.reactants:
			s = react.name
			if react.percent:
				s += "\t%i%% by mol" % (react.percent)
			else:
				s += "\tUnknown% by mol"
			print "adding:", s
			self.ui.reactantList.addItem(s)

		for prod in self.products:
			s = prod.name
			if prod.percent:
				s += "\t%i%% yield" % (prod.percent)
			else:
				s += "\tUnknown% yield"
			print "adding:", s
			self.ui.productList.addItem(s)
		
		self.catalysts = self.reaction.catalysts
		self.ui.catalystList.addItems([i.name for i in self.catalysts])

		if self.reaction.reference:
			self.ui.referenceLineEdit.setText(self.reaction.reference.title)
			self.ui.referenceLineEdit.setCursorPosition(0)
			self.ui.refAuthorsLineEdit.setText(self.reaction.reference.author_name)
			self.ui.refAuthorsLineEdit.setCursorPosition(0)
			self.ui.refYearSpinBox.setValue(self.reaction.reference.year)
			self.ui.referenceUnknownCheckBox.setChecked(False)
		if self.reaction.temperature:
			self.ui.temperatureSpinBox.setValue(self.reaction.temperature)
			self.ui.temperatureUnknownCheckBox.setChecked(False)
		else:
			self.ui.temperatureUnknownCheckBox.setChecked(True)
		if self.reaction.pressure:
			self.ui.pressureSpinBox.setValue(self.reaction.pressure)
			self.ui.pressureUnitsComboBox.setCurrentIndex(0)
			self.ui.pressureUnknownCheckBox.setChecked(False)
		else:
			self.ui.pressureUnknownCheckBox.setChecked(True)
		if self.reaction.time:
			self.ui.daysSpinBox.setValue(int(self.reaction.time/60/24))
			self.ui.hoursSpinBox.setValue(int(self.reaction.time/60)%24)
			self.ui.minutesSpinBox.setValue(int(self.reaction.time%60))
			self.ui.secondsSpinBox.setValue((self.reaction.time-int(self.reaction.time))*60)
			self.ui.timeUnknownCheckBox.setChecked(False)
		else:
			self.ui.timeUnknownCheckBox.setChecked(True)
		if self.reaction.pH:
			self.ui.pHSpinBox.setValue(self.reaction.pH)
			self.ui.pHUnknownCheckBox.setChecked(False)
		else:
			self.ui.pHUnknownCheckBox.setChecked(True)
		if self.reaction.solvent:
			self.ui.solventLineEdit.setText(self.reaction.solvent)
			self.ui.solventUnknownCheckBox.setChecked(False)
		else:
			self.ui.solventUnknownCheckBox.setChecked(True)
		if self.reaction.solid:
			self.ui.solidCheckBox.setChecked(True)
		if self.reaction.liquid:
			self.ui.liquidCheckBox.setChecked(True)
		if self.reaction.gas:
			self.ui.gasCheckBox.setChecked(True)
		if self.reaction.other:
			self.ui.otherLineEdit.setText(self.reaction.other)
			self.ui.otherUnknownCheckBox.setChecked(False)
		else:
			self.ui.otherUnknownCheckBox.setChecked(True)

	def setReadonly(self, readonly):
		self.readonly = readonly

		self.ui.temperatureSpinBox.setReadOnly(readonly)
		self.ui.pressureSpinBox.setReadOnly(readonly)
		self.ui.daysSpinBox.setReadOnly(readonly)
		self.ui.hoursSpinBox.setReadOnly(readonly)
		self.ui.minutesSpinBox.setReadOnly(readonly)
		self.ui.secondsSpinBox.setReadOnly(readonly)
		self.ui.pHSpinBox.setReadOnly(readonly)
		self.ui.solventLineEdit.setReadOnly(readonly)
		self.ui.solidCheckBox.setEnabled(not readonly)
		self.ui.liquidCheckBox.setEnabled(not readonly)
		self.ui.gasCheckBox.setEnabled(not readonly)
		self.ui.otherLineEdit.setReadOnly(readonly)

		self.ui.temperatureUnknownCheckBox.setEnabled(not readonly)
		self.ui.pressureUnknownCheckBox.setEnabled(not readonly)
		self.ui.timeUnknownCheckBox.setEnabled(not readonly)
		self.ui.pHUnknownCheckBox.setEnabled(not readonly)
		self.ui.solventUnknownCheckBox.setEnabled(not readonly)
		self.ui.otherUnknownCheckBox.setEnabled(not readonly)
		self.ui.referenceUnknownCheckBox.setEnabled(not readonly)

		self.ui.searchCatalystPushButton.setEnabled(not readonly)
		self.ui.searchProductPushButton.setEnabled(not readonly)
		self.ui.searchReactantPushButton.setEnabled(not readonly)
		self.ui.removeCatalystPushButton.setEnabled(not readonly)
		self.ui.removeProductPushButton.setEnabled(not readonly)
		self.ui.removeReactantPushButton.setEnabled(not readonly)
		self.ui.editReactantPushButton.setEnabled(not readonly)
		self.ui.editProductPushButton.setEnabled(not readonly)
		self.ui.searchReferencePushButton.setEnabled(not readonly)
		self.ui.newCatalystPushButton.setEnabled(not readonly)
		self.ui.showAllCatalystsPushButton.setEnabled(not readonly)
		self.ui.newReferencePushButton.setEnabled(not readonly)

		self.ui.addPushButton.setEnabled(not readonly)
		
		if readonly:
			self.ui.updatePushButton.hide()
		else:
			self.ui.updatePushButton.show()

	def setMode(self, mode):
		self.mode = mode
		if self.mode == "Adding":
			self.ui.addPushButton.show()
			self.ui.updatePushButton.hide()
		elif self.mode == "Update":
			self.ui.addPushButton.hide()
			if self.readonly:
				self.ui.updatePushButton.hide()
			else:
				self.ui.updatePushButton.show()

	def searchCatalyst(self):
		self.controller.catalystDialog("Search")

	def newCatalyst(self):
		self.controller.catalystDialog("New")

	def showAllCatalysts(self):
		catalyst = self.catalystClass()
		catalyst.name = "%"
		self.controller.searchCatalyst(catalyst)

	def searchProduct(self):
		self.searchCompound("Product")

	def searchReactant(self):
		self.searchCompound("Reactant")

	def searchCompound(self, mode):
		self.controller.searchCompoundDialog(mode)

	def addToList(self, obj, where):
		if where == "Reactant":
			if obj in self.reactants:
				msgDialog = QtGui.QMessageBox(QtGui.QMessageBox.Critical, "Adding Error", "Compound already in list", QtGui.QMessageBox.Ok)
				msgDialog.exec_()
				return
			react = self.equCompoundClass(obj, 0)
			self.reactants.append(obj)
			name = react.name
			if len(name) > self.maxCompoundLength:
				name = name[:self.maxCompoundLength]+'...'
			self.ui.reactantList.addItem(name + "\tUnknown% by mol")
		elif where == "Product":
			if obj in self.products:
				msgDialog = QtGui.QMessageBox(QtGui.QMessageBox.Critical, "Adding Error", "Compound already in list", QtGui.QMessageBox.Ok)
				msgDialog.exec_()
				return
			prod = self.equCompoundClass(obj, 0)
			self.products.append(prod)
			name = prod.name
			if len(name) > self.maxCompoundLength:
				name = name[:self.maxCompoundLength]+'...'
			self.ui.productList.addItem(name + "\tUnknown% yield")
		elif where == "Catalyst":
			if obj in self.catalysts:
				msgDialog = QtGui.QMessageBox(QtGui.QMessageBox.Critical, "Adding Error", "Catalyst already in list", QtGui.QMessageBox.Ok)
				msgDialog.exec_()
				return
			self.catalysts.append(obj)
			name = obj.name
			if len(name) > self.maxCompoundLength+20:
				name = name[:self.maxCompoundLength]+'...'
			self.ui.catalystList.addItem(name)
		elif where == "Reference":
			self.reference = obj
			self.ui.referenceLineEdit.setText(self.reference.title)
			self.ui.refAuthorsLineEdit.setText(self.reference.author_name)
			self.ui.refYearSpinBox.setValue(self.reference.year)
			self.ui.referenceLineEdit.setCursorPosition(0)
			self.ui.refAuthorsLineEdit.setCursorPosition(0)

	def searchReference(self):
		self.controller.referenceDialog("Search")

	def newReference(self):
		self.controller.referenceDialog("New")

	def removeReactant(self):
		row = self.ui.reactantList.currentRow()
		del self.reactants[row]
		self.ui.reactantList.takeItem(row)

	def removeProduct(self):
		row = self.ui.productList.currentRow()
		del self.products[row]
		self.ui.productList.takeItem(row)

	def removeCatalyst(self):
		row = self.ui.catalystList.currentRow()
		del self.catalysts[row]
		self.ui.catalystList.takeItem(row)

	def pressureUnitChanged(self, units):
		units = str(units)
		pressure = float(self.ui.pressureSpinBox.value()) / pressureConversions[self.units]
		pressure *= pressureConversions[units]
		self.units = units
		self.ui.pressureSpinBox.setValue(pressure)

	def editProduct(self):
		self.productDoubleClick(self.ui.productList.currentRow())
		
	def editReactant(self):
		self.reactantDoubleClick(self.ui.reactantList.currentRow())

	def reactantDoubleClick(self, listItem):
		row = self.ui.reactantList.row(listItem)
		reactant = self.reactants[row]
		name = reactant.name
		oldPercent = reactant.percent
		percent,ok = QtGui.QInputDialog.getInteger(self, "Percent Composition", "Please input the mol % of " + name, oldPercent, 0, 100, 1)
		if not ok:
			return

		s = 0
		for react in self.reactants:
			s += react.percent

		if s + percent-oldPercent > 100:
			msgDialog = QtGui.QMessageBox(QtGui.QMessageBox.Critical, "Format Error", "Percents add to more than 100", QtGui.QMessageBox.Ok)
			msgDialog.exec_()
			return

		reactant.percent = percent
		listItem.setText(name + "\t%i%% by mol" % (percent))

	def productDoubleClick(self, listItem):
		row = self.ui.productList.row(listItem)
		product = self.products[row]
		name = product.name
		oldPercent = product.percent
		percent,ok = QtGui.QInputDialog.getInteger(self, "Percent Composition", "Please input the % yield of " + name, oldPercent, 0, 100, 1)
		if not ok:
			return

		s = 0
		for prod in self.products:
			s += prod.percent

		if s + percent-oldPercent > 100:
			msgDialog = QtGui.QMessageBox(QtGui.QMessageBox.Critical, "Format Error", "Percents add to more than 100", QtGui.QMessageBox.Ok)
			msgDialog.exec_()
			return

		product.percent = percent
		listItem.setText(name + "\t%i%% yield" % (percent))

	# These are hacks so I don't have to write new classes
	def customReactantDropEvent(self, event):
		searchVeiw = event.source().parent()
		if searchVeiw.mode == "Adding Reactant" or searchVeiw.mode == "Adding Product":
			compound = searchVeiw.getSelected()
			self.addToList(compound, "Reactant")

	def customProductDropEvent(self, event):
		searchVeiw = event.source().parent()
		if searchVeiw.mode == "Adding Reactant" or searchVeiw.mode == "Adding Product":
			compound = searchVeiw.getSelected()
			self.addToList(compound, "Product")

	def customCatalystDropEvent(self, event):
		searchVeiw = event.source().parent()
		if searchVeiw.mode == "Adding Catalyst":
			compound = searchVeiw.getSelected()
			self.addToList(compound, "Catalyst")